/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bgu.sonar.util.ui;

import bgu.sonar.util.async.AsyncTask;
import bgu.sonar.util.ui.mon.Monitor;
import bgu.sonar.util.ui.mon.MonitorView;
import bgu.sonar.util.ui.mon.SimpleMonitor;
import java.awt.BorderLayout;
import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 *
 * @author bennyl
 */
public abstract class AsyncUILoader extends AsyncTask<Void> {

    private static final HashMap<String, AsyncUILoader> pendingTasks = new HashMap<>();
    private JPanel[] panels;
    private Component[] originalContent;
    private Exception exception = null;
    private String name;
    private Monitor monitor = null;

    public AsyncUILoader(String taskName, boolean useMonitor, JPanel... panelsToBlock) {
        this.panels = panelsToBlock;
        this.name = taskName;
        if (useMonitor) {
            monitor = new SimpleMonitor(taskName);
        }
    }

    public Monitor getMonitor() {
        return monitor;
    }

    public AsyncUILoader(String taskName, Monitor monitor, JPanel... panelsToBlock) {
        this.panels = panelsToBlock;
        this.name = taskName;
        this.monitor = monitor;
    }

    protected abstract void load() throws Exception;

    protected abstract void updateUI(boolean success);

    protected Exception getException() {
        return exception;
    }

    @Override
    public Void call() throws Exception {
        try {
            synchronized (pendingTasks) {
                AsyncUILoader old = pendingTasks.put(name, this);
                if (old != null) {
                    old.cancel();
                    old.awaitTerminationNoResult();
                }
            }

            blockWithProgress();
            try {
                load();
            } catch (Exception ex) {
                exception = ex;
                if (isCanceled()) {
                    return null;
                }
            }

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    unblockWithProgress();
                    updateUI(exception == null);
                }
            });
        } finally {
            synchronized (pendingTasks) {
                if (pendingTasks.get(name) == this) {
                    pendingTasks.remove(name);
                }
            }
            return null;
        }
    }

    private void blockWithProgress() throws InterruptedException, InvocationTargetException {
        originalContent = new Component[panels.length];
        for (int i = 0; i < panels.length; i++) {
            originalContent[i] = panels[i].getComponent(0);
        }

        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                for (JPanel g : panels) {
                    g.removeAll();
                    if (monitor == null) {
                        g.add(new ProgressGlassPane(), BorderLayout.CENTER);
                    } else {
                        g.add(new MonitorView(monitor), BorderLayout.CENTER);
                    }

                    g.invalidate();
                    g.revalidate();
                    g.repaint();
                }
            }
        });
    }

    private void unblockWithProgress() {
        for (int i = 0; i < panels.length; i++) {
            panels[i].removeAll();
            panels[i].add(originalContent[i], BorderLayout.CENTER);
            panels[i].invalidate();
            panels[i].revalidate();
            panels[i].repaint();
        }
    }
}
